﻿using System.Text;
using FarseerGames.AdvancedSamples.DrawingSystem;
using FarseerGames.AdvancedSamples.ScreenSystem;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Dynamics.Springs;
using FarseerGames.FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using FarseerGames.AdvancedSamples.Demos.DemoShare;

namespace FarseerGames.AdvancedSamples.Demos.Demo6
{
    public class Demo6Screen : GameScreen
    {
        private Texture2D _chainTexture;
        private Texture2D _wheelTexture;
        private Vector2 _chainOrigin;
        private Vector2 _wheelOrigin;
        private Path _track;
        private Vertices _controlPoints;
        private Body _wheel1;
        private Body _wheel2;
        private Geom _wheelg;
        private LinearSpring _spring;

        // shapes
        private BodyList _obstacles;
        private GeomList _obstaclesg;
        private GeomList _obstaclesp1;

        // shapes
        private Texture2D _squaresTexture;
        private Vector2 _squaresOrigin;
        private Texture2D _circlesTexture;
        private Vector2 _circlesOrigin;
        private Texture2D _vOblongsTexture;
        private Vector2 _vOblongsOrigin;
        private Texture2D _hOblongsTexture;
        private Vector2 _hOblongsOrigin;
        private Texture2D _polygonTexture;
        private Vector2 _polygonOrigin;

        private Border _border;
        private LineBrush _lineBrush = new LineBrush(1, Color.Black); //used to draw spring on mouse grab
        private FixedLinearSpring _mousePickSpring;
        private Geom _pickedGeom;

        private int shape;
        private bool stat;
        private int maxShapes = 5;
        private Body dragged;
        private Vertices verts;

        private Vector2 deltaMovement;
        private Vector2 position;
        private int CursorSpeed = 600;

        public override void Initialize()
        {
            PhysicsSimulator = new PhysicsSimulator(new Vector2(0, 100));
            PhysicsSimulatorView = new PhysicsSimulatorView(PhysicsSimulator);

            base.Initialize();
        }

        public override void LoadContent()
        {
            // to add textures use
            // _someTexture =  ScreenManager.ContentManager.Load<Texture2D>("Content/textureName");
            // and drag 'n drop texture into Content on left


            // custom shapes

            _squaresTexture = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 40, 40, Color.LawnGreen,
                                                                 Color.Black);

            _circlesTexture = DrawingHelper.CreateCircleTexture(ScreenManager.GraphicsDevice, 20, 2, Color.Azure, Color.Black);

            _vOblongsTexture = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 20, 80, Color.Bisque,
                                                                 Color.Black);

            _hOblongsTexture = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 80, 20, Color.MediumAquamarine,
                                                                 Color.Black);

            _polygonTexture = ScreenManager.ContentManager.Load<Texture2D>("Content/Slope");
            //Create an array to hold the data from the texture
            uint[] data = new uint[_polygonTexture.Width * _polygonTexture.Height];
            //Transfer the texture data to the array
            _polygonTexture.GetData(data);
            //Calculate the vertices from the array
            verts = Vertices.CreatePolygon(data, _polygonTexture.Width, _polygonTexture.Height);
            //Make sure that the origin of the texture is the centroid (real center of geometry)
            _polygonOrigin = verts.GetCentroid();

            _obstaclesp1 = new GeomList();
            
            // non custom shapes

            _chainTexture = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 20, 10, Color.White,
                                                                 Color.Black);

            _wheelTexture = DrawingHelper.CreateCircleTexture(ScreenManager.GraphicsDevice, 45, 2, Color.White, Color.Black);

            _chainOrigin = new Vector2(_chainTexture.Width / 2f, _chainTexture.Height / 2f);
            _wheelOrigin = new Vector2(_wheelTexture.Width / 2f, _wheelTexture.Height / 2f);
            _squaresOrigin = new Vector2(_squaresTexture.Width / 2f, _squaresTexture.Height / 2f);
            _circlesOrigin = new Vector2(_circlesTexture.Width / 2f, _circlesTexture.Height / 2f);
            _vOblongsOrigin = new Vector2(_vOblongsTexture.Width / 2f, _vOblongsTexture.Height / 2f);
            _hOblongsOrigin = new Vector2(_hOblongsTexture.Width / 2f, _hOblongsTexture.Height / 2f);
            
            _border = new Border(ScreenManager.ScreenWidth, ScreenManager.ScreenHeight, 25, ScreenManager.ScreenCenter);
            _border.Load(ScreenManager.GraphicsDevice, PhysicsSimulator);
            Vector2 center = new Vector2(400, 500);

            _controlPoints = new Vertices();
            _controlPoints.Add(new Vector2(-15, -50) + center);
            _controlPoints.Add(new Vector2(-50, -50) + center);
            _controlPoints.Add(new Vector2(-100, -25) + center);
            _controlPoints.Add(new Vector2(-100, 25) + center);
            _controlPoints.Add(new Vector2(-50, 50) + center);
            _controlPoints.Add(new Vector2(50, 50) + center);
            _controlPoints.Add(new Vector2(100, 25) + center);
            _controlPoints.Add(new Vector2(100, -25) + center);
            _controlPoints.Add(new Vector2(50, -50) + center);
            _controlPoints.Add(new Vector2(-10, -50) + center);

            _track = ComplexFactory.Instance.CreateTrack(PhysicsSimulator, _controlPoints, 20.0f, 10.0f, 3.0f, true, 2, LinkType.RevoluteJoint);

            foreach (Geom g in _track.Geoms)
                g.FrictionCoefficient = 1.0f;

            _wheel1 = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 45, 30);
            _wheel1.Position = new Vector2(-50, 0) + center;
            _wheel2 = BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 45, 30);
            _wheel2.Position = new Vector2(50, 0) + center;

            _wheelg = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, _wheel1, 45, 36);
            _wheelg.FrictionCoefficient = 1.0f;
            _wheelg = GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, _wheel2, 45, 36);
            _wheelg.FrictionCoefficient = 1.0f;

            _spring = SpringFactory.Instance.CreateLinearSpring(PhysicsSimulator, _wheel1, new Vector2(), _wheel2, new Vector2(), 1200, 250);
            _spring.RestLength += 20;

            _lineBrush.Load(ScreenManager.GraphicsDevice);

            _obstacles = new BodyList();
            _obstaclesg = new GeomList();


            base.LoadContent();

            shape = 1;
            stat = true;
        }

        public override void Draw(GameTime gameTime)
        {
            ScreenManager.SpriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            deltaMovement = GamePad.GetState(PlayerIndex.One).ThumbSticks.Left;
            deltaMovement.Y *= -1;

            #if !XBOX360
            //use the mouse position as the cursor position
            MouseState mouseState = Mouse.GetState();
            position.X = mouseState.X;
            position.Y = mouseState.Y;
            #endif

            // modify position using delta, the CursorSpeed, and
            // the elapsed game time.
            position += deltaMovement * CursorSpeed *
                (float)gameTime.ElapsedGameTime.TotalSeconds;

            #if XBOX360
            // clamp the cursor position to the viewport, so that it can't move off the
            // screen.
            Viewport vp = GraphicsDevice.Viewport;
            position.X = MathHelper.Clamp(position.X, vp.X, vp.X + vp.Width);
            position.Y = MathHelper.Clamp(position.Y, vp.Y, vp.Y + vp.Height);
            #else
            // set the new mouse position using the combination of mouse and gamepad data.
            Mouse.SetPosition((int)position.X, (int)position.Y);    
            #endif            
            deltaMovement = GamePad.GetState(PlayerIndex.One).ThumbSticks.Left;
            deltaMovement.Y *= -1;


            foreach (Body body in _track.Bodies)
            {
                ScreenManager.SpriteBatch.Draw(_chainTexture, body.Position, null, Color.White, body.Rotation, _chainOrigin, 1, SpriteEffects.None, 1);
            }

            ScreenManager.SpriteBatch.Draw(_wheelTexture, _wheel1.Position, null, Color.White, _wheel1.Rotation, _wheelOrigin, 1, SpriteEffects.None, 1);
            ScreenManager.SpriteBatch.Draw(_wheelTexture, _wheel2.Position, null, Color.White, _wheel2.Rotation, _wheelOrigin, 1, SpriteEffects.None, 1);

            // shapes

            if(stat)
                ScreenManager.SpriteBatch.DrawString(ScreenManager.SpriteFonts.DiagnosticSpriteFont,
                                                 "Static",
                                                 new Vector2(875, 200), Color.White);
            else
                ScreenManager.SpriteBatch.DrawString(ScreenManager.SpriteFonts.DiagnosticSpriteFont,
                                                 "Free to move",
                                                 new Vector2(875, 200), Color.White);

            foreach (Body b in _obstacles)
            {
                if (b.shape == 1)
                    ScreenManager.SpriteBatch.Draw(_squaresTexture, b.Position, null, Color.White, b.Rotation, _squaresOrigin, 1, SpriteEffects.None, 1);
                if (b.shape == 2)
                    ScreenManager.SpriteBatch.Draw(_circlesTexture, b.Position, null, Color.White, b.Rotation, _circlesOrigin, 1, SpriteEffects.None, 1);
                if (b.shape == 3)
                    ScreenManager.SpriteBatch.Draw(_vOblongsTexture, b.Position, null, Color.White, b.Rotation, _vOblongsOrigin, 1, SpriteEffects.None, 1);
                if (b.shape == 4)
                    ScreenManager.SpriteBatch.Draw(_hOblongsTexture, b.Position, null, Color.White, b.Rotation, _hOblongsOrigin, 1, SpriteEffects.None, 1);
                if (b.shape == 5)
                    ScreenManager.SpriteBatch.Draw(_polygonTexture, b.Position, null, Color.White, b.Rotation, _polygonOrigin, 1, SpriteEffects.None, 1);
            }

            Vector2 itemPos = new Vector2(900, 100);            
            if (shape == 1)
                ScreenManager.SpriteBatch.Draw(_squaresTexture, itemPos, null, Color.White, 0, _squaresOrigin, 1, SpriteEffects.None, 1);
            if (shape == 2)
                ScreenManager.SpriteBatch.Draw(_circlesTexture, itemPos, null, Color.White, 0, _circlesOrigin, 1, SpriteEffects.None, 1);
            if (shape == 3)
                ScreenManager.SpriteBatch.Draw(_vOblongsTexture, itemPos, null, Color.White, 0, _vOblongsOrigin, 1, SpriteEffects.None, 1);
            if (shape == 4)
                ScreenManager.SpriteBatch.Draw(_hOblongsTexture, itemPos, null, Color.White, 0, _hOblongsOrigin, 1, SpriteEffects.None, 1);
            if (shape == 5)
                ScreenManager.SpriteBatch.Draw(_polygonTexture, itemPos, null, Color.White, 0, _polygonOrigin, 1, SpriteEffects.None, 1);


            _border.Draw(ScreenManager.SpriteBatch);
            if (_mousePickSpring != null)
            {
                _lineBrush.Draw(ScreenManager.SpriteBatch,
                                _mousePickSpring.Body.GetWorldPosition(_mousePickSpring.BodyAttachPoint),
                                _mousePickSpring.WorldAttachPoint);
            }

            ScreenManager.SpriteBatch.End();

            base.Draw(gameTime);
        }

        float _torque;

        public override void HandleInput(InputState input)
        {
            if (FirstRun)
            {
                ScreenManager.AddScreen(new PauseScreen(GetTitle(), GetDetails(), this));
                FirstRun = false;
            }
            if (input.PauseGame)
            {
                ScreenManager.AddScreen(new PauseScreen(GetTitle(), GetDetails(), this));
            }
            else
            {
                HandleMouseInput(input);
            }
 
            if (input.CurrentKeyboardState.IsKeyDown(Keys.S) && input.LastKeyboardState.IsKeyUp(Keys.S))
                stat = !stat;

            // shape change
            if (input.CurrentKeyboardState.IsKeyDown(Keys.PageUp) && input.LastKeyboardState.IsKeyUp(Keys.PageUp))
            {
                if (shape < maxShapes)
                    shape++;
                else shape = 1;
            }
            else if (input.CurrentKeyboardState.IsKeyDown(Keys.PageDown) && input.LastKeyboardState.IsKeyUp(Keys.PageDown))
            {
                if (shape > 1)
                    shape--;
                else shape = maxShapes;
            }

            // do some keyboard torque stuff
            if (input.CurrentKeyboardState.IsKeyDown(Keys.Left))
            {
                _torque = -3500;
            }
            else if (input.CurrentKeyboardState.IsKeyDown(Keys.Right))
            {
                _torque = 3500;
            }
            else
            {
                _torque = 0;
                _wheel1.ClearTorque();
                _wheel2.ClearTorque();
            }

                       
            /*
            if(input.CurrentGamePadState.IsButtonDown(Buttons.Start) && 
                input.LastGamePadState.IsButtonUp(Buttons.Start) )
            {
                ScreenManager.AddScreen(new PauseScreen(GetTitle(), GetDetails(), this));
            }
            */
            if (input.CurrentGamePadState.IsButtonDown(Buttons.LeftThumbstickLeft))
            {
                _torque = -5000;
            }
            if (input.CurrentGamePadState.IsButtonDown(Buttons.LeftThumbstickRight))
            {
                _torque = 5000;
            }





            _wheel1.ApplyAngularImpulse(_torque);
            _wheel2.ApplyAngularImpulse(_torque);
            base.HandleInput(input);
        }

        private void HandleMouseInput(InputState input)
        {
            Vector2 point = new Vector2(input.CurrentMouseState.X, input.CurrentMouseState.Y);

            if (input.LastMouseState.RightButton == ButtonState.Released &&
                input.CurrentMouseState.RightButton == ButtonState.Pressed)
            {
                _pickedGeom = PhysicsSimulator.Collide(point);
                if (_pickedGeom != null)
                {
                    _obstacles.Remove(_pickedGeom.Body);
                    _pickedGeom.Body.Dispose();
                }

            }

            // if dragging static
            if (input.LastMouseState.LeftButton == ButtonState.Pressed &&
                input.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                if (dragged != null)
                {
                    if (dragged.IsStatic && dragged.shape >= 1 && dragged.shape <= maxShapes)
                    {
                        dragged.Position = new Vector2(input.CurrentMouseState.X, input.CurrentMouseState.Y);
                    }
                }
            }

            if (input.LastMouseState.LeftButton == ButtonState.Pressed &&
                input.CurrentMouseState.LeftButton == ButtonState.Released)
            {
                dragged = null;
            }


            if (input.LastMouseState.LeftButton == ButtonState.Released &&
                input.CurrentMouseState.LeftButton == ButtonState.Pressed)
            {
                //create mouse spring
                _pickedGeom = PhysicsSimulator.Collide(point);
                if (_pickedGeom != null)
                {
                    // if static shape
                    if (_pickedGeom.Body.IsStatic && _pickedGeom.Body.shape >= 1 && _pickedGeom.Body.shape <= maxShapes)
                    {
                        dragged = _pickedGeom.Body;
                        //_pickedGeom.Body.Position = new Vector2(input.CurrentMouseState.X, input.CurrentMouseState.Y);
                    }

                    // if not static
                    if (!_pickedGeom.Body.IsStatic)
                        _mousePickSpring = SpringFactory.Instance.CreateFixedLinearSpring(PhysicsSimulator,
                                                                                          _pickedGeom.Body,
                                                                                          _pickedGeom.Body.
                                                                                              GetLocalPosition(point),
                                                                                          point, 150, 10);
                }
                else
                {
                    // shapes

                    if (shape == 1)
                    {
                        _obstacles.Add(BodyFactory.Instance.CreateRectangleBody(PhysicsSimulator, 40, 40, 25)); 
                        _obstaclesg.Add(GeomFactory.Instance.CreateRectangleGeom(PhysicsSimulator, _obstacles[_obstacles.Count - 1], 40, 40));  
                    }
                    
                    if (shape == 2)
                    {
                        _obstacles.Add(BodyFactory.Instance.CreateCircleBody(PhysicsSimulator, 45, 30));
                        _obstaclesg.Add(GeomFactory.Instance.CreateCircleGeom(PhysicsSimulator, _obstacles[_obstacles.Count - 1], 45, 30));
                    }

                    if (shape == 3)
                    {
                        _obstacles.Add(BodyFactory.Instance.CreateRectangleBody(PhysicsSimulator, 20, 80, 30));
                        _obstaclesg.Add(GeomFactory.Instance.CreateRectangleGeom(PhysicsSimulator, _obstacles[_obstacles.Count - 1], 20, 80));
                    }

                    if (shape == 4)
                    {
                        _obstacles.Add(BodyFactory.Instance.CreateRectangleBody(PhysicsSimulator, 80, 20, 30));
                        _obstaclesg.Add(GeomFactory.Instance.CreateRectangleGeom(PhysicsSimulator, _obstacles[_obstacles.Count - 1], 80, 20));
                    }

                    if (shape == 5)
                    {
                        // here
                        _obstacles.Add(BodyFactory.Instance.CreatePolygonBody(PhysicsSimulator, verts, 5));
                        _obstaclesg.Add(GeomFactory.Instance.CreatePolygonGeom(PhysicsSimulator, _obstacles[_obstacles.Count - 1], verts, 0));
                    }

                    _obstacles[_obstacles.Count - 1].IsStatic = stat;
                    _obstacles[_obstacles.Count - 1].Position = new Vector2(input.CurrentMouseState.X, input.CurrentMouseState.Y);
                    _obstacles[_obstacles.Count - 1].shape = shape;
                    _obstaclesg[_obstaclesg.Count - 1].FrictionCoefficient = 1.0f;
                    if (stat)
                        dragged = _obstacles[_obstacles.Count - 1];
                }
            }
            else if (input.LastMouseState.LeftButton == ButtonState.Pressed &&
                     input.CurrentMouseState.LeftButton == ButtonState.Released)
            {
                //destroy mouse spring
                if (_mousePickSpring != null && _mousePickSpring.IsDisposed == false)
                {
                    _mousePickSpring.Dispose();
                    _mousePickSpring = null;
                }
            }

            //move anchor point
            if (input.CurrentMouseState.LeftButton == ButtonState.Pressed && _mousePickSpring != null)
            {
                _mousePickSpring.WorldAttachPoint = point;
            }
        }


        public static string GetTitle()
        {
            return "Editor";
        }

        public static string GetDetails()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Level Editor");
            sb.AppendLine("PageUp or PageDown to change object");
            sb.AppendLine("on editor brush.");
            sb.AppendLine("Click to add objects.");
            sb.AppendLine("Press S to toggle between");
            sb.AppendLine("static and free objects.");
            sb.AppendLine(string.Empty);
            sb.AppendLine("Keyboard:");
            sb.AppendLine("  -Move : left and right arrows");
            return sb.ToString();
        }
    }
}
